package com.jyh.utils;

import com.jyh.domain.PageInfo;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
 * @Author JYH
 * @Date 2024/8/23 13:48
 * @Description 这是一个专门用于数据库操作的工具类, 功能比JDBCUtils更强大
 * 特点:1.所有的表都通用,每次操作之前只需要将db.properties文件中的url中的数据库指定好即可
 * 2.程序员只需要传入: sql,    用于封装表中数据的字节码对象, sql中的 ? 占位符的参数即可    返回数据集合或者影响的行数
 */
public final class BaseDAO {
    private BaseDAO() {
    }
    //6.分页查询某数据库中任意一张表的多条数据
    // select * from student where address = ? limit (pageNum-1)*pageSize , pageSize
    public static <T> PageInfo<T> findByPage(String sql , Class<T> c , Integer pageNum , Integer pageSize , Object... params) throws Exception {
        // 1. 参数校验
        if (sql == null || "".equals(sql) || sql.length() == 0 ||  c == null) {
            throw new IllegalArgumentException("参数非法！");
        }
        // 2. 将sql进行分页的拼接改装
        sql = sql  + " limit "+(pageNum - 1) * pageSize +" , "+pageSize+" ";

        // 调用 selectMultipleRow () 发送和执行sql
        List<T> list = selectMultipleRow(sql, c, params);

        // 封装PageInfo
        PageInfo<T> pageInfo = new PageInfo<>(list, list.size());
        return pageInfo;
    }

    //5.查询某数据库中任意一张表的多条：有可能查询所有,也有可能带条件查询多条
    public static <T> List<T> selectMultipleRow(String sql, Class<T> c, Object... params){
        Connection connection = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            //1.参数校验
            if (sql == null || "".equals(sql) || sql.length() == 0 || c==null) {
                throw new IllegalArgumentException("参数非法！");
            }
            //2.获取数据库连接对象
            connection = JDBCUtil.getConnection();
            //3.获取预编译sql的发送和执行sql的对象
            ps = connection.prepareStatement(sql);
            //4.使用实际参数替换ps对应的sql中的?占位符
            for (int i = 0; i < params.length; i++) {
                ps.setObject(i+1,params[i]);
            }
            //5.发送和执行sql
            rs = ps.executeQuery();
            //根据rs获取标题字段
            ResultSetMetaData metaData = rs.getMetaData();//代表标题列
            //通过MetaData获取标题的列数
            int columnCount = metaData.getColumnCount();
            //准备一个List集合
            List<T> list = new ArrayList<>();
            //获取每一列的属性名和属性值
            while (rs.next()){
                //获取c对应的实例对象
                Constructor<T> declaredConstructor = c.getDeclaredConstructor();
                T t = declaredConstructor.newInstance();
                //1.获取标题的属性名
                for (int i = 1; i <= columnCount; i++) {
                    String columnName = metaData.getColumnName(i);
                    Object columnValue = rs.getObject(i);
                    //2.根据columnName获取每一列的属性对象
                    Field declaredField = c.getDeclaredField(columnName);
                    //3.打开暴力访问开关,将columnValue设置到t对象的declaredField中
                    declaredField.setAccessible(true);
                    declaredField.set(t,columnValue);
                }
                //每一行的数据都封装到t中之后,将t对象存入集合
                list.add(t);
            }
            return list;
        } catch (Exception e) {

            e.printStackTrace();
        }finally {
            JDBCUtil.close(rs,ps,connection);
        }
        return null;
    }

    //4.根据id删除某数据库中任意一张表中一条或多条数据,要么同时成功,要么同时失败
    public static int deleteMultipleRow(String sql, Integer... ids) {
        Connection connection = null;
        PreparedStatement ps = null;
        try {
            //1.数据校验,异常则暴露异常
            if (sql == null || "".equals(sql) || sql.length() == 0 || ids.length == 0) {
                throw new IllegalArgumentException("参数非法！");
            }
            //2.获取数据库连接对象
            connection = JDBCUtil.getConnection();
            //3.获取预编译sql的发送和执行sql的对象
            ps = connection.prepareStatement(sql);
            //关闭自动提交事务
            connection.setAutoCommit(false);
            //4.遍历ids,封装一个个的sql并添加到批处理容器
            for (Integer id : ids) {
                ps.setObject(1, id);
                //将封装好的sql添加到批处理容器
                ps.addBatch();
            }
            //一次性发送和提交容器
            int[] result = ps.executeBatch();
            //获取影响的行数
            int count = 0;
            for (int item : result) {
                if (count == 1) {
                    count++;
                } else {//执行失败,暴露异常
                    throw new SQLException("其中一条或多条sql执行失败!");
                }
            }
            //手动提交事务
            connection.commit();
            return count;
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            //只要有一条sql执行失败就进行回滚
            try {
                connection.rollback();
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
            e.printStackTrace();
        } finally {
            //关闭资源
            JDBCUtil.close(ps, connection);
        }
        return 0;
    }

    //3.向某数据库中的任意一张表中修改一条或多条数据
    public static int updateMultipleRow(String sql, Object... params) {
        Connection connection = null;
        PreparedStatement ps = null;
        try {
            //1.数据校验,异常则暴露异常
            if (sql == null || "".equals(sql) || sql.length() == 0) {
                throw new IllegalArgumentException("参数非法！");
            }
            //2.获取数据库连接对象
            connection = JDBCUtil.getConnection();
            //3.获取预编译的发送和执行sql语句的对象
            ps = connection.prepareStatement(sql);
            //4.将实际参数值：params分别替换sql中的?占位符
            for (int i = 0; i < params.length; i++) {
                ps.setObject(i + 1, params[i]);
            }
            //5.发送和执行sql
            int count = ps.executeUpdate();
            //返回影响的行数
            return count;
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            //6.关流
            JDBCUtil.close(ps, connection);
        }
        return 0;
    }

    //2.向某数据库的任意一张表中删除一条数据
    public static int deleteOneRow(String sql, Object... params) {
        Connection connection = null;
        PreparedStatement ps = null;
        try {
            //1.数据校验,异常则暴露异常
            if (sql == null || "".equals(sql) || sql.length() == 0) {
                throw new IllegalArgumentException("参数非法！");
            }
            //2.获取数据库连接对象
            connection = JDBCUtil.getConnection();
            //3.获取预编译的发送和执行sql语句的对象
            ps = connection.prepareStatement(sql);
            //4.将实际参数值：params分别替换sql中的?占位符
            for (int i = 0; i < params.length; i++) {
                ps.setObject(i + 1, params[i]);
            }
            //5.发送和执行sql
            int count = ps.executeUpdate();
            //返回影响的行数
            return count;
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            //6.关流
            JDBCUtil.close(ps, connection);
        }
        return 0;
    }

    //1.向某数据库的任意一张表中添加一条数据, Object... --->可变参数
    public static int insertOneRow(String sql, Object... params) {
        Connection connection = null;
        PreparedStatement ps = null;
        try {
            //1.数据校验,异常则暴露异常
            if (sql == null || "".equals(sql) || sql.length() == 0) {
                throw new IllegalArgumentException("参数非法！");
            }
            //2.获取数据库连接对象
            connection = JDBCUtil.getConnection();
            //3.获取预编译的发送和执行sql语句的对象
            ps = connection.prepareStatement(sql);
            //4.将实际参数值：params分别替换sql中的?占位符
            for (int i = 0; i < params.length; i++) {
                ps.setObject(i + 1, params[i]);
            }
            //5.发送和执行sql
            int count = ps.executeUpdate();
            //返回影响的行数
            return count;
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            //6.关流
            JDBCUtil.close(ps, connection);
        }
        return 0;
    }
}
